package com.jzo2o.orders.manager.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.db.DbRuntimeException;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.customer.AddressBookApi;
import com.jzo2o.api.customer.dto.response.AddressBookResDTO;
import com.jzo2o.api.foundations.dto.response.ServeAggregationResDTO;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.model.msg.TradeStatusMsg;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.DateUtils;
import com.jzo2o.common.utils.NumberUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.orders.base.config.OrderStateMachine;
import com.jzo2o.orders.base.enums.OrderPayStatusEnum;
import com.jzo2o.orders.base.enums.OrderStatusChangeEventEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.base.model.dto.OrderSnapshotDTO;
import com.jzo2o.orders.manager.ClientApi.ClientApi;
import com.jzo2o.orders.manager.ClientApi.CustomerClient;
import com.jzo2o.orders.manager.model.dto.request.OrdersPayReqDTO;
import com.jzo2o.orders.manager.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.orders.manager.model.dto.response.OrdersPayResDTO;
import com.jzo2o.orders.manager.model.dto.response.PlaceOrderResDTO;
import com.jzo2o.orders.manager.porperties.TradeProperties;
import com.jzo2o.orders.manager.service.IOrdersCreateService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.undertow.util.BadRequestException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

import static com.jzo2o.common.constants.ErrorInfo.Code.TRADE_FAILED;
import static com.jzo2o.orders.base.constants.RedisConstants.Lock.ORDERS_SHARD_KEY_ID_GENERATOR;
import static com.jzo2o.orders.base.enums.OrderStatusEnum.DISPATCHING;

/**
 * <p>
 * 下单服务类
 * </p>
 *
 * @author itcast
 * @since 2023-07-10
 */
@Slf4j
@Service
public class OrdersCreateServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersCreateService {
@Resource
    CustomerClient customerClient;
@Resource
    ClientApi clientApi;
    @Resource
    private RedisTemplate<String, Long> redisTemplate;
    @Resource
    private IOrdersCreateService owner;
    @Resource
    private OrderStateMachine orderStateMachine;

    @Override
    public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO dto) throws BadRequestException {
        //入数据库-参数分析-来源拼凑-入库

        //下单人信息，获取地址簿，调用jzo2o-customer服务获取
        Long addressBookId = dto.getAddressBookId();
        AddressBookResDTO detail1 = customerClient.getDetail(addressBookId);
        if(detail1==null)
        {
            throw new BadRequestException("预约地址异常，无法下单");
        }
        //服务相关信息,调用jzo2o-foundations获取
        ServeAggregationResDTO serveResDTO = clientApi.getServeApi(dto.getServeId());
        //生成订单号
        Long id = generateOrderId();
        //计算价格

        //组装订单信息，插入数据库订单表，订单状态为待支付
        Orders orders = new Orders();
        // userId，从threadLocal获取当前登录用户的id，通过UserContextInteceptor拦截进行设置
        orders.setUserId(UserContext.currentUserId());
        // 服务id
        orders.setServeId(dto.getServeId());
// 服务项id
        orders.setServeItemId(serveResDTO.getServeItemId());
        orders.setServeItemName(serveResDTO.getServeItemName());
        orders.setServeItemImg(serveResDTO.getServeItemImg());
        orders.setUnit(serveResDTO.getUnit());
//服务类型信息
        orders.setServeTypeId(serveResDTO.getServeTypeId());
        orders.setServeTypeName(serveResDTO.getServeTypeName());
        // 订单状态
        orders.setOrdersStatus(0);
        // 支付状态，暂不支持，初始化一个空状态
        orders.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());
        // 服务时间
        orders.setServeStartTime(dto.getServeStartTime());
        // 城市编码
        orders.setCityCode(serveResDTO.getCityCode());
        // 地理位置
        orders.setLon(detail1.getLon());
        orders.setLat(detail1.getLat());
//地址拼凑
        String serveAddress = new StringBuffer(detail1.getProvince())
                .append(detail1.getCity())
                .append(detail1.getCounty())
                .append(detail1.getAddress())
                .toString();
        orders.setServeAddress(serveAddress);
        // 联系人
        orders.setContactsName(detail1.getName());
        orders.setContactsPhone(detail1.getPhone());
        // 价格
        orders.setPrice(serveResDTO.getPrice());
        // 购买数量
        orders.setPurNum(NumberUtils.null2Default(dto.getPurNum(), 1));
        // 订单总金额 价格 * 购买数量
        orders.setTotalAmount(orders.getPrice().multiply(new BigDecimal(orders.getPurNum())));
        // 优惠金额 当前默认0
        orders.setDiscountAmount(BigDecimal.ZERO);
        // 实付金额 订单总金额 - 优惠金额
        orders.setRealPayAmount(NumberUtils.sub(orders.getTotalAmount(), orders.getDiscountAmount()));
        long sortBy = DateUtils.toEpochMilli(orders.getServeStartTime()) + orders.getId() % 100000;
        orders.setSortBy(sortBy);
        owner.Saveorders(orders);


        return new PlaceOrderResDTO(orders.getId());

    }
    @Transactional(rollbackFor = Exception.class)
   public void Saveorders(Orders orders)
    {
        boolean save = this.save(orders);

        if (!save) {
            throw new DbRuntimeException("下单失败");
        }
        //初始化状态机
        OrderSnapshotDTO orderSnapshotDTO=BeanUtil.toBean(orders,OrderSnapshotDTO.class);
        ////
        orderStateMachine.start(null, String.valueOf(orders.getId()),orderSnapshotDTO);

    }

    /**
     *
     * @param id   订单id
     * @param ordersPayReqDTO 订单支付请求体
     * @return 订单支付响应体
     */
    @Override
    public OrdersPayResDTO pay(Long id, OrdersPayReqDTO ordersPayReqDTO) {
        //1.查询订单
       Orders orders= baseMapper.selectById(id);
       if(ObjectUtil.isNull(orders))
       {
           throw new CommonException(TRADE_FAILED, "订单不存在");

       }
       //2.订单-支付成功-待支付 -重复支付问题-时小性
        if(OrderPayStatusEnum.PAY_SUCCESS.getStatus()==orders.getPayStatus()   &&
        ObjectUtil.isNotEmpty(orders.getTradingOrderNo())
        )
        {
            OrdersPayResDTO ordersPayResDTO=new OrdersPayResDTO();
            BeanUtil.copyProperties(orders, ordersPayResDTO);
            ordersPayResDTO.setProductOrderNo(orders.getId());
            return ordersPayResDTO;

        }else
        {
            //未支付- 远程调用支付服务获取响应
            NativePayResDTO nativePayResDTO=generateQrCode(orders,ordersPayReqDTO.getTradingChannel());
            OrdersPayResDTO ordersPayResDTO=BeanUtil.toBean(nativePayResDTO,OrdersPayResDTO.class);
            return ordersPayResDTO;
        }

    }

    /**
     * 查询支付结果
     * @param id
     * @return
     */
    @Override
    public OrdersPayResDTO payResult(Long id) {
        OrdersPayResDTO payResultFromTradServer = getPayResultFromTradServer(id);

        return payResultFromTradServer;
    }
    @Resource
    private TradingApi tradingApi;
    /**
     * 请求支付服务查询支付结果
     * @param id 订单id
     * @return
     */

    @Override
    public OrdersPayResDTO getPayResultFromTradServer(Long id) {
        //1.查询订单表
        Orders orders = baseMapper.selectById(id);
        if(ObjectUtil.isNotNull(orders))
        {
            throw new CommonException(TRADE_FAILED, "订单不存在");
        }
        //2.拿出支付结果
        Integer payStatus = orders.getPayStatus();
        //未支付-远程调用
        if(OrderPayStatusEnum.NO_PAY.getStatus() == payStatus && ObjectUtil.isNotEmpty(orders.getTradingOrderNo()))
        {
            TradingResDTO tradingResDTO = tradingApi.findTradResultByTradingOrderNo(orders.getId());
            if(ObjectUtil.isNotNull(tradingResDTO)&& ObjectUtil.equals(tradingResDTO.getTradingState(), TradingStateEnum.YJS))
            {
                //如果支付成功
                ///1.构造信息
                TradeStatusMsg msg=TradeStatusMsg.builder().productOrderNo(orders.getId())
                        .tradingChannel(tradingResDTO.getTradingChannel())
                        .statusCode(TradingStateEnum.YJS.getCode())
                        .tradingOrderNo(tradingResDTO.getTradingOrderNo())
                        .transactionId(tradingResDTO.getTransactionId())
                        .build();
                owner.paySuccess(msg);
                //构造返回信息
                OrdersPayResDTO ordersPayResDTO= BeanUtils.toBean(msg,OrdersPayResDTO.class);
                ordersPayResDTO.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
                return  ordersPayResDTO;

            }


        }
        //未支付
        OrdersPayResDTO ordersPayResDTO=new OrdersPayResDTO();
        ordersPayResDTO.setPayStatus(payStatus);
        ordersPayResDTO.setProductOrderNo(orders.getId());
        ordersPayResDTO.setTradingOrderNo(orders.getTradingOrderNo());
        ordersPayResDTO.setTradingChannel(orders.getTradingChannel());
        return ordersPayResDTO;


    }

    /**
     * 支付成功， 更新数据库的订单表及其他信息
     * @param tradeStatusMsg 交易状态消息
     */

    @Override
    @Transactional
    public void paySuccess(TradeStatusMsg tradeStatusMsg) {
        //修改订单数据库为已支付
        Orders orders=baseMapper.selectById(tradeStatusMsg.getProductAppId());
        if(ObjectUtil.isNull(orders))
        {
            throw new CommonException(TRADE_FAILED,"订单不存在");
        }
        //校验支付状态如果不是待支付状态则不作处理
        if(ObjectUtil.notEqual(OrderPayStatusEnum.NO_PAY.getStatus(), orders.getPayStatus()))
        {
            log.info("更新订单支付成功，当前订单:{}支付状态不是待支付状态", orders.getId());
            return;
        }
        //校验订单状态如果不是待支付状态则不作处理
        if (ObjectUtils.notEqual(OrderStatusEnum.NO_PAY.getStatus(),orders.getOrdersStatus())) {
            log.info("更新订单支付成功，当前订单:{}状态不是待支付状态", orders.getId());
        }
        //第三方支付单号校验
        if (ObjectUtil.isEmpty(tradeStatusMsg.getTransactionId())) {
            throw new CommonException("支付成功通知缺少第三方支付单号");
        }

        ///状态机变更 未支付 -支付 OrderStatusChangeEventEnum.PAYED, orderSnapshotDTOB

        OrderSnapshotDTO orderSnapshotDTO = OrderSnapshotDTO.builder()
                .payTime(LocalDateTime.now())
                .tradingOrderNo(tradeStatusMsg.getTradingOrderNo())
                .tradingChannel(tradeStatusMsg.getTradingChannel())
                .thirdOrderId(tradeStatusMsg.getTransactionId())
                .build();

        orderStateMachine.changeStatus(null, OrderStatusChangeEventEnum.PAYED,orderSnapshotDTO);



    }

    /**
     * 超时任务获取
     * @param count 获取的数量
     * @return
     */
    @Override
    public List<Orders> queryOverTimePayOrdersListByCount(Integer count) {
        //拿出数据的合规订单进行时间过滤取出
        List<Orders> list=lambdaQuery().eq
                (Orders::getOrdersStatus,OrderStatusEnum.NO_PAY.getStatus()).lt
                (Orders::getCreateTime,LocalDateTime.now().minusMinutes(15))
                .last("limit"+count).list();
        return list;
    }

    @Resource
    private TradeProperties tradeProperties;
    @Resource
    private NativePayApi nativePayApi;
    /**
     *
     * @param orders 订单信息
     * @param  tradingChannel 是否切换支付
     * @return 远程调用返回的响应
     */
    private NativePayResDTO generateQrCode(Orders orders, PayChannelEnum tradingChannel) {
        String zffs = tradingChannel.getValue();

        NativePayReqDTO nativePayReqDTO = new NativePayReqDTO();
        nativePayReqDTO.setEnterpriseId(Long.valueOf(zffs));
        //业务系统标识
        nativePayReqDTO.setProductAppId("jzo2o.orders");
        //家政订单号
        nativePayReqDTO.setProductOrderNo(orders.getId());
        //支付渠道
        nativePayReqDTO.setTradingChannel(tradingChannel);
        //支付金额
        nativePayReqDTO.setTradingAmount(orders.getRealPayAmount());
        //备注信息
        nativePayReqDTO.setMemo(orders.getServeItemName());
        //判断是否切换支付渠道
        if (ObjectUtil.isNotEmpty(orders.getTradingChannel())
                && ObjectUtil.notEqual(orders.getTradingChannel(), tradingChannel.toString())) {
            nativePayReqDTO.setChangeChannel(true);
        }
        //生成二维码
        NativePayReqDTO nativePayDTO=new NativePayReqDTO();
        BeanUtil.copyProperties(nativePayReqDTO, nativePayDTO);
        NativePayResDTO downLineTrading = nativePayApi.createDownLineTrading(nativePayDTO);
        if(ObjectUtil.isNotNull(downLineTrading))
        {
            boolean update=lambdaUpdate().eq(Orders::getId,orders.getId())
                    .set(Orders::getTradingOrderNo,downLineTrading.getTradingOrderNo())
                    .set(Orders::getTradingChannel,downLineTrading.getTradingChannel())
                    .update();
            if(!update)
            {
                throw new CommonException("订单:"+orders.getId()+"请求支付更新交易单号失败");
            }
        }

        return downLineTrading;
    }


    private Long generateOrderId() {
        ///用于对指定键的值进行自增操作 -自带锁性质
        Long increment = redisTemplate.opsForValue().increment(ORDERS_SHARD_KEY_ID_GENERATOR, 1);
        long orderId = DateUtils.getFormatDate(LocalDateTime.now(), "yyMMdd") * 10000000000000L + increment;


        return orderId;
    }

}
